package db

import (
	"database/sql"
	"encoding/json"
	"errors"
	"log"
	"sync"
	"time"
	"ulcdkey/tool"
)

// cdk类表缓存
type CdkTypeCache struct {
	Data map[int]map[int]*CdkType
	Lock sync.RWMutex
}

func NewCdkTypeCache() *CdkTypeCache {
	var nMap = new(CdkTypeCache)
	nMap.Data = make(map[int]map[int]*CdkType)
	return nMap
}

// 更新cdk类缓存
func RefreshCdkTypeCache() (err error) {
	temp := make(map[int]map[int]*CdkType)

	var games = new(Game)
	gs, err := games.GetGameList()
	for _, g := range gs {
		var nCdkType = new(CdkType)
		cdkMap := make(map[int]*CdkType)

		nCdkType.GameId = g.Id
		//根据 游戏id查询cdk类
		as, _, err := nCdkType.GetCdkTypeListByGameId(0, 0)
		if err != nil {
			return err
		}
		//组装cdk类map
		for _, a := range as {
			cdkMap[a.Id] = a
		}
		//组装map
		temp[g.Id] = cdkMap
	}
	CdkTypeCacheMap.Lock.Lock()
	defer CdkTypeCacheMap.Lock.Unlock()
	CdkTypeCacheMap.Data = temp
	return
}

// 根据游戏id，cdktypeId
func ReadCdkTypeCacheByIds(gameId, cdkTypeId int) *CdkType {
	CdkTypeCacheMap.Lock.RLock()
	defer CdkTypeCacheMap.Lock.RUnlock()
	return CdkTypeCacheMap.Data[gameId][cdkTypeId]
}

// 根据游戏ID读取cdk类缓存
func ReadCdkTypeCacheByGameId(gameId int) map[int]*CdkType {
	CdkTypeCacheMap.Lock.RLock()
	defer CdkTypeCacheMap.Lock.RUnlock()
	return CdkTypeCacheMap.Data[gameId]
}

// 读取cdk类缓存
func ReadCdkTypeCache() map[int]map[int]*CdkType {
	CdkTypeCacheMap.Lock.RLock()
	defer CdkTypeCacheMap.Lock.RUnlock()
	return CdkTypeCacheMap.Data
}

/* -------------------------------------------------------------- */

// 获取所有cdk类列表
func (this *CdkType) GetCdkTypeList() (as []*CdkType, err error) {
	rows, err := db.Query("select id,`name`,gameId,channelId,totalCount,canUseCount,userLimitCount,bUseTimes,startDate,endDate,`desc`,rewardList,gameVersion from cdktype")
	if err != nil {
		return
	}
	defer rows.Close()
	for rows.Next() {
		a := new(CdkType)
		var rewardList, channelId string
		err = rows.Scan(&a.Id, &a.Name, &a.GameId, &channelId, &a.TotalCount, &a.CanUseCount, &a.UserLimitCount, &a.BUseTimes, &a.StartDate, &a.EndDate, &a.Desc, &rewardList, &a.GameVersion)
		if err != nil {
			return
		}

		err = json.Unmarshal([]byte(rewardList), &a.RewardList)
		if err != nil {
			return
		}

		err = json.Unmarshal([]byte(channelId), &a.ChannelId)
		if err != nil {
			return
		}

		as = append(as, a)
	}

	return
}

// 获取cdk类列表
func (this *CdkType) GetCdkTypeListByGameId(page, limit int) (as []*CdkType, counts int, err error) {
	var rows *sql.Rows
	if page == 0 && limit == 0 {
		// 不进行分页查询
		rows, err = db.Query("select id,`name`,gameId,channelId,totalCount,canUseCount,userLimitCount,bUseTimes,startDate,endDate,`desc`,rewardList,gameVersion,createUser,createTime from cdktype where gameId = ? ", this.GameId)
	} else {
		// 分页查询
		rows, err = db.Query("select id,`name`,gameId,channelId,totalCount,canUseCount,userLimitCount,bUseTimes,startDate,endDate,`desc`,rewardList,gameVersion,createUser,createTime from cdktype where gameId = ? limit ?,?", this.GameId, (page-1)*limit, limit)
	}

	if err != nil {
		return
	}

	defer rows.Close()
	for rows.Next() {
		a := new(CdkType)
		a.ChannelId = new(CdkTypeChannelInfo)
		var rewardList, channelId string
		err = rows.Scan(&a.Id, &a.Name, &a.GameId, &channelId, &a.TotalCount, &a.CanUseCount, &a.UserLimitCount, &a.BUseTimes, &a.StartDate, &a.EndDate, &a.Desc, &rewardList, &a.GameVersion, &a.CreateUserId, &a.CreateTime)
		if err != nil {
			return
		}
		err = json.Unmarshal([]byte(rewardList), &a.RewardList)
		if err != nil {
			log.Println("rewardList json Unmarshal error,cdktypeId=", a.Id)
			return
		}

		err = json.Unmarshal([]byte(channelId), &a.ChannelId)
		if err != nil {
			log.Println("GetCdkTypeListByGameId Unmarshal err:", channelId)
			return
		}

		// 创建人
		user, ok := UserList[a.CreateUserId]
		if ok {
			a.CreateUserName = user.Name
		}

		as = append(as, a)
	}

	/* 总条数 */
	err = db.QueryRow("select count(1) from cdktype where gameId = ?", this.GameId).Scan(&counts)
	if err != nil {
		return
	}
	return
}

//检查物品id是否重复
func (this *CdkType) checkItemId() (err error) {

	itemIds := make(map[int]bool)
	for i := 0; i < len(this.RewardList); i++ {

		reward := this.RewardList[i]
		if itemIds[reward.GoodsId] {

			err = errors.New("物品id重复")
			return
		} else {
			itemIds[reward.GoodsId] = true
		}
	}

	return
}

//检查有效时间 是否小于  720天
func (this *CdkType) checkValidTime() (err error) {

	startTime, err := time.ParseInLocation("2006-01-02 15:04:05", this.StartDate, time.Local)
	if err != nil {

		return
	}

	endTime, err := time.ParseInLocation("2006-01-02 15:04:05", this.EndDate, time.Local)
	if err != nil {

		return
	}

	hours := endTime.Sub(startTime).Hours()
	validHours := float64(365 * 2 * 24) // 两年

	log.Println("checkValidTime hours :", hours, " validHours:", validHours)

	if hours > validHours {

		err = errors.New("cdk有效期最大两年")
		return
	}

	return
}

// 增加cdk类
func (this *CdkType) AddCdkType(info *tool.VisitUserInfo) (err error) {

	err = this.checkItemId()
	if err != nil {

		return
	}

	err = this.checkValidTime()
	if err != nil {

		return
	}

	bytes, err := json.Marshal(this.RewardList)
	if err != nil {
		return
	}
	rewardListStr := string(bytes)

	channelBytes, err := json.Marshal(this.ChannelId)
	if err != nil {
		return
	}
	channelIdStr := string(channelBytes)

	row, err := db.Exec("insert into cdktype (`name`,gameId,channelId,totalCount,canUseCount,userLimitCount,bUseTimes,startDate,endDate,`desc`,rewardList,gameVersion,createUser) values (?,?,?,?,?,?,?,?,?,?,?,?,?)",
		this.Name, this.GameId, channelIdStr, this.TotalCount, this.TotalCount, this.UserLimitCount, this.BUseTimes, this.StartDate, this.EndDate, this.Desc, rewardListStr, this.GameVersion, info.Uid)
	if err != nil {
		return
	}

	int64s, err := row.LastInsertId()
	if err != nil {
		return
	}
	this.Id = int(int64s)

	// 更新缓存
	err = RefreshCdkTypeCache()
	if err != nil {
		return
	}

	// 记录日志
	nb, _ := json.Marshal(this)
	serverLog := &ServerLog{UserId: info.Uid, UserName: info.UserName, Platform: info.Platform, ActionType: LogActionType.Add, Module: "CDK类管理", OldContent: "", NewContent: string(nb)}
	serverLog.RecordLog()
	return
}

// 更新cdk类
func (this *CdkType) UpdateCdkType(info *tool.VisitUserInfo) (err error) {

	err = this.checkItemId()
	if err != nil {

		return
	}

	err = this.checkValidTime()
	if err != nil {

		return
	}

	old, err := this.GetCdkTypeById(this.Id)
	if err != nil {
		return
	}

	// 如果cdk类已经失效，则不再允许修改
	oldEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", old.EndDate, time.Local)
	if err != nil {
		return err
	}

	if time.Now().After(oldEndTime) {
		return errors.New("cdk类已失效，请重新创建")
	}

	bytes, err := json.Marshal(this.RewardList)
	if err != nil {
		return
	}
	rewardListStr := string(bytes)

	channelBytes, err := json.Marshal(this.ChannelId)
	if err != nil {
		return
	}
	channelIdStr := string(channelBytes)

	nCanUseCount := (this.TotalCount - old.TotalCount) + old.CanUseCount
	if nCanUseCount < 0 {
		return errors.New("设置后该类cdk可使用条数小于0")
	}
	_, err = db.Exec("update cdktype set `name` = ?,gameId = ?,channelId = ?,totalCount = ?,canUseCount = ?,userLimitCount = ?,bUseTimes = ?,startDate = ?,endDate = ?,`desc` = ?,rewardList = ? where id = ?",
		this.Name, this.GameId, channelIdStr, this.TotalCount, nCanUseCount, this.UserLimitCount, this.BUseTimes, this.StartDate, this.EndDate, this.Desc, rewardListStr, this.Id)
	if err != nil {
		return
	}

	// 更新缓存
	err = RefreshCdkTypeCache()
	if err != nil {
		return
	}

	// 记录日志
	ob, _ := json.Marshal(old)
	nb, _ := json.Marshal(this)
	serverLog := &ServerLog{UserId: info.Uid, UserName: info.UserName, Platform: info.Platform, ActionType: LogActionType.Update, Module: "CDK类管理", OldContent: string(ob), NewContent: string(nb)}
	serverLog.RecordLog()

	return
}

// 删除cdk类
func (this *CdkType) DelCdkType(info *tool.VisitUserInfo) (err error) {
	old, err := this.GetCdkTypeById(this.Id)
	if err != nil {
		log.Println("DelCdkType 1 error", err)
		return
	}

	_, err = db.Exec("delete from cdktype where id = ?", this.Id)
	if err != nil {
		log.Println("DelCdkType 2 error", err)
		return
	}

	/* games := new(Game)
	nGames, err := games.GetGameById(old.GameId)
	if err != nil {
		log.Println("DelCdkType 3 error", err)
		return
	}
	//删除cdk清单表中该类cdk
	sqlStr := fmt.Sprintf("delete from ulcdkey_cdk.cdk_%s_%s where cdkTypeId = %d", nGames.Platform, nGames.GameCode, this.Id)
	_, err = db.Exec(sqlStr)
	if err != nil {
		log.Println("DelCdkType 4 error", err)
		return
	}
	//删除cdk使用表中该类cdk
	sqlStr2 := fmt.Sprintf("delete from ulcdkey_cdkuse.cdkuse_%s_%s where cdkTypeId = %d", nGames.Platform, nGames.GameCode, this.Id)
	_, err = db.Exec(sqlStr2)
	if err != nil {
		log.Println("DelCdkType 5 error", err)
		return
	} */

	// 更新缓存
	err = RefreshCdkTypeCache()
	if err != nil {
		return
	}

	// 记录日志
	ob, _ := json.Marshal(old)
	serverLog := &ServerLog{UserId: info.Uid, UserName: info.UserName, Platform: info.Platform, ActionType: LogActionType.Delete, Module: "CDK类管理", OldContent: string(ob), NewContent: ""}
	serverLog.RecordLog()

	return
}

// 根据id获取cdk类
func (this *CdkType) GetCdkTypeById(cdkTypeId int) (res *CdkType, err error) {

	res = new(CdkType)
	var rewardListStr, channelId string
	err = db.QueryRow("select id,`name`,gameId,channelId,totalCount,canUseCount,userLimitCount,bUseTimes,startDate,endDate,`desc`,rewardList from cdktype where id = ? ", cdkTypeId).Scan(
		&res.Id, &res.Name, &res.GameId, &channelId, &res.TotalCount, &res.CanUseCount, &res.UserLimitCount, &res.BUseTimes, &res.StartDate, &res.EndDate, &res.Desc, &rewardListStr)
	if err != nil {
		return
	}
	err = json.Unmarshal([]byte(rewardListStr), &res.RewardList)
	if err != nil {
		return
	}

	err = json.Unmarshal([]byte(channelId), &res.ChannelId)
	if err != nil {
		return
	}
	return
}

// 获取cdk类对应游戏的gameCode
func (this *CdkType) GetCdkTypeGameCode() (gameCode string, err error) {
	nGames := new(Game)
	sGames, err := nGames.GetGameById(this.GameId)
	if err != nil {
		return
	}

	gameCode = sGames.GameCode
	return
}

//校验兑换的cdk 类是否有该渠道
func (this *CdkType) checkChannelId(platform string, channelId string) (flag bool) {

	var channelIds []string

	//android h5 ios
	if platform == "android" {
		channelIds = this.ChannelId.Android
	} else if platform == "h5" {
		channelIds = this.ChannelId.H5
	} else {
		channelIds = this.ChannelId.IOS
	}

	for i := 0; i < len(channelIds); i++ {
		idStr := channelIds[i]
		if channelId == idStr {
			//渠道合法
			flag = true
			return
		}
	}

	return
}
